package com.penglecode.netty.server;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import com.penglecode.netty.NettyException;

public class NettyServer {

	private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
	
	private static final ChannelGroup allChannels = new DefaultChannelGroup("netty-server");
	
	private ServerBootstrap serverBootstrap;
	
	private ChannelPipelineFactory channelPipelineFactory;
	
	private int port;
	
	public void setPort(int port) {
		this.port = port;
	}

	public void setServerBootstrap(ServerBootstrap serverBootstrap) {
		this.serverBootstrap = serverBootstrap;
	}

	public void setChannelPipelineFactory(
			ChannelPipelineFactory channelPipelineFactory) {
		this.channelPipelineFactory = channelPipelineFactory;
	}

	public NettyServer() {
		super();
	}
	
	public NettyServer(int port) {
		super();
		Assert.isTrue(this.port > 0 && this.port < 65536, "Invalid port number!");
		this.port = port;
	}
	
	public void init(){
		Assert.isTrue(this.port > 0 && this.port < 65536, "Invalid port number!");
		Assert.notNull(this.channelPipelineFactory, "channelPipelineFactory must be required!");
		if(this.serverBootstrap == null){
			this.serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
		}
		this.serverBootstrap.setPipelineFactory(this.channelPipelineFactory);
		
		this.serverBootstrap.setOption("child.tcpNoDelay", true);
		this.serverBootstrap.setOption("child.keepAlive", true);
		this.serverBootstrap.setOption("reuseAddress", true);
	}
	
	public void start() {
		try {
			Channel channel = this.serverBootstrap.bind(new InetSocketAddress(InetAddress.getLocalHost(), this.port));
			allChannels.add(channel);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new NettyException(e.getMessage(), e);
		}
	}

	public void stop() {
		allChannels.close().awaitUninterruptibly();
		this.serverBootstrap.shutdown();
		this.serverBootstrap.releaseExternalResources();
	}
	
}
